//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.springboot.ww.config.dynamic;

import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import com.baomidou.dynamic.datasource.ds.AbstractRoutingDataSource;
import com.baomidou.dynamic.datasource.ds.ItemDataSource;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidConfig;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.springboot.ww.config.context.TenantContext;
import com.springboot.ww.dto.TenantDbDTO;
import com.springboot.ww.dto.TenantDbListDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.SmartLifecycle;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;

public class TenantDynamicRoutingDataSource extends AbstractRoutingDataSource implements SmartLifecycle, DisposableBean {
    private static final Logger log = LoggerFactory.getLogger(TenantDynamicRoutingDataSource.class);
    private final Map<String, DataSource> dataSourceMap = new ConcurrentHashMap(64);
    private Map<String, Object> dataSourceLockMap = new ConcurrentHashMap(64);
    private TenantDynamicDataSourceProvider provider;
    private boolean running = false;

    public TenantDynamicRoutingDataSource() {
    }

    protected DataSource determineDataSource() {
        return this.getDataSource(TenantContext.getTID(), DynamicDataSourceContextHolder.peek());
    }

    private DataSource getDataSource(String tenantId, String ds) {
        if (!StringUtils.isEmpty(ds)) {
            if (log.isDebugEnabled()) {
                log.debug("动态切换[{}]应用数据源路由，当前租户[{}]，切换[{}]数据源", new Object[]{this.provider.APP_CODE, tenantId, ds});
            }

            return this.dataSourceMap.containsKey(ds) ? (DataSource) this.dataSourceMap.get(ds) : this.getDataSource(ds);
        } else {
            return this.getDataSource(tenantId);
        }
    }

    private DataSource getDataSource(String ds) {
        if (StringUtils.isEmpty(ds)) {
            throw new RuntimeException(MessageFormat.format("动态切换[{0}]应用数据源路由，租户标识为空", this.provider.APP_CODE));
        } else if (this.dataSourceMap.containsKey(ds)) {
            if (log.isDebugEnabled()) {
                log.debug("动态切换[{}]应用数据源路由，切换到租户ID[{}]的数据源", this.provider.APP_CODE, ds);
            }

            return (DataSource) this.dataSourceMap.get(ds);
        } else {
            return this.createDataSource(ds, (DataSourceProperty) null);
        }
    }

    private DataSource createDataSource(String ds, DataSourceProperty dataSourceProperty) {
        Object tenantLock = this.dataSourceLockMap.get(ds);
        if (Objects.isNull(tenantLock)) {
            Object tempTenantLock = new Object();
            synchronized (tempTenantLock) {
                tenantLock = this.dataSourceLockMap.get(ds);
                if (Objects.isNull(tenantLock)) {
                    tenantLock = tempTenantLock;
                    this.dataSourceLockMap.put(ds, tenantLock);
                }
            }
        }

        synchronized (tenantLock) {
            DataSource tenantDataSource = (DataSource) this.dataSourceMap.get(ds);
            if (Objects.nonNull(tenantDataSource)) {
                log.warn("动态切换[{}]应用数据源路由，创建租户ID[{}]的数据源实例，实例已存在，不重复创建", this.provider.APP_CODE, ds);
                return tenantDataSource;
            } else {
                String appCode = this.provider.APP_CODE;
                if (Objects.isNull(dataSourceProperty)) {
                    String result = HttpRequest.get("http://opms-service.box-tools.com/client/tenant/db_config?appCode=" + this.provider.APP_CODE + "&tenantId=" + ds).execute().body();
                    TenantDbDTO tenantDbDTO = JSONUtil.toBean(result, TenantDbDTO.class);
                    TenantDbDTO.DataBean dataBean;
                    if (Objects.isNull(tenantDbDTO) || Objects.isNull(dataBean = tenantDbDTO.getData())) {
                        throw new RuntimeException(MessageFormat.format("动态切换[{0}]应用数据源路由，创建租户ID[{1}]的数据源实例，加载DB配置信息为空", this.provider.APP_CODE, ds));
                    }

                    dataSourceProperty = this.provider.getDataSourceProperty(ds);
                    DruidConfig druid = new DruidConfig();
                    druid.setFilters("");
                    dataSourceProperty.setUrl(dataBean.getDbUrl())
                            .setUsername(dataBean.getUsername())
                            .setPassword(dataBean.getPassword())
                            .setDruid(druid);;

                    try {
                        tenantDataSource = this.provider.createDataSource(ds, dataSourceProperty);
                        if (Objects.isNull(tenantDataSource)) {
                            throw new RuntimeException(MessageFormat.format("动态切换[{0}]应用数据源路由，创建租户ID[{1}]的数据源实例失败", this.provider.APP_CODE, ds));
                        }
                    } catch (Exception var12) {
                        throw new RuntimeException(MessageFormat.format("动态切换[{0}]应用数据源路由，创建租户ID[{1}]的数据源实例失败", this.provider.APP_CODE, ds));
                    }
                } else {
                    try {
                        tenantDataSource = this.provider.createDataSource(ds, dataSourceProperty);
                        if (Objects.isNull(tenantDataSource)) {
                            throw new RuntimeException(MessageFormat.format("动态切换[{0}]应用数据源路由，创建租户ID[{1}]的数据源实例失败", this.provider.APP_CODE, ds));
                        }
                    } catch (Exception var11) {
                        Exception e = var11;
                        throw new RuntimeException(MessageFormat.format("动态切换[{0}]应用数据源路由，创建租户ID[{1}]的数据源实例失败：{2}", this.provider.APP_CODE, ds, e.getMessage()));
                    }
                }

//                log.info("动态切换[{}]应用数据源路由，创建租户ID[{}]的数据源实例成功...", appCode, ds);
//                try{
//                    Map<String, DataSource> singleDataSourceMap = new HashMap<>();
//                    singleDataSourceMap.put("master",((ItemDataSource) tenantDataSource).getDataSource());
//                    DataSource shardingDataSource = ShardingSphereDataSourceFactory.createDataSource(ds, null, singleDataSourceMap, ruleConfigs, null);
//                    this.dataSourceMap.put(ds, shardingDataSource);
//                    return shardingDataSource;
//                }catch (Exception e){
//                    log.error("创建数据源实例异常", e);
//                }
                return tenantDataSource;
            }
        }
    }

    public void start() {
        log.info("服务启动，加载[{}]应用数据源，开始...", this.provider.APP_CODE);
        Map<String, DataSource> dataSources = this.provider.loadDataSources();
        Iterator var2 = dataSources.entrySet().iterator();

        while (var2.hasNext()) {
            Map.Entry<String, DataSource> dsItem = (Map.Entry) var2.next();
//            try {
//                Map<String, DataSource> singleDataSourceMap = new HashMap<>();
//                singleDataSourceMap.put("master",((ItemDataSource) dsItem.getValue()).getDataSource());
//                DataSource shardingDataSource = ShardingSphereDataSourceFactory.createDataSource(dsItem.getKey(), null, singleDataSourceMap, ruleConfigs, null);
//                dataSourceMap.put(dsItem.getKey(), shardingDataSource);
//            } catch (Exception e) {
//                log.error("创建数据源实例异常", e);
//                throw new RuntimeException(e);
//            }
            dataSourceMap.put(dsItem.getKey(), dsItem.getValue());
        }



        StringJoiner sj = new StringJoiner(",");
        Iterator var6 = this.dataSourceMap.entrySet().iterator();

        while (var6.hasNext()) {
            Map.Entry<String, DataSource> dsItem = (Map.Entry) var6.next();
            sj.add((CharSequence) dsItem.getKey());
        }

        log.info("服务启动，加载[{}]应用，共[{}]个数据源", this.provider.APP_CODE, this.dataSourceMap.size());
        log.info("================================================================");
        log.info("======== 加载的数据源有：{}========", sj.toString());
        log.info("================================================================");
        this.running = true;
    }

    public void stop() {
        this.running = false;
    }

    public boolean isRunning() {
        return this.running;
    }

    private void closeDataSource(DataSource dataSource) throws Exception {
        if (dataSource instanceof ItemDataSource) {
            ((ItemDataSource) dataSource).close();
        } else {
            Class<? extends DataSource> clazz = dataSource.getClass();
            Method closeMethod = clazz.getDeclaredMethod("close");
            closeMethod.invoke(dataSource);
        }

    }

    public void destroy() throws Exception {
        log.info("动态数据源启动关闭...");
        Iterator var1 = this.dataSourceMap.entrySet().iterator();

        while (var1.hasNext()) {
            Map.Entry<String, DataSource> item = (Map.Entry) var1.next();
            this.closeDataSource((DataSource) item.getValue());
        }

        log.info("动态数据源关闭成功，拜拜...");
    }




    public void setProvider(final TenantDynamicDataSourceProvider provider) {
        this.provider = provider;
    }
}
